﻿using System;
using System.Threading;
using System.Timers;

namespace fastCSharp.threading
{
    /// <summary>
    /// 定时任务信息
    /// </summary>
    public sealed class timerTask : IDisposable
    {
        /// <summary>
        /// 任务处理线程池
        /// </summary>
        private threadPool threadPool;
        /// <summary>
        /// 已排序任务
        /// </summary>
        private arrayHeap<DateTime, taskInfo> taskHeap = new arrayHeap<DateTime, taskInfo>();
        /// <summary>
        /// 定时器
        /// </summary>
        private System.Timers.Timer timer = new System.Timers.Timer();
        /// <summary>
        /// 最近时间
        /// </summary>
        private DateTime nearTime = DateTime.MaxValue;
        /// <summary>
        /// 任务访问锁
        /// </summary>
        private readonly object taskLock = new object();
        /// <summary>
        /// 线程池任务
        /// </summary>
        private Action threadTaskHandle;
        /// <summary>
        /// 定时任务信息
        /// </summary>
        /// <param name="threadPool">任务处理线程池</param>
        public timerTask(threadPool threadPool)
        {
            this.threadPool = threadPool ?? threadPool.TinyPool;
            timer.Elapsed += onTimer;
            timer.AutoReset = false;
            threadTaskHandle = threadTask;
        }
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            if (timer != null)
            {
                timer.Stop();
                timer.Dispose();
                timer = null;
            }
            taskHeap = null;
        }
        /// <summary>
        /// 添加新任务
        /// </summary>
        /// <param name="run">任务执行委托</param>
        /// <param name="onError">任务执行出错委托,停止任务参数null</param>
        /// <param name="runTime">执行时间</param>
        private void add(Action run, Action<Exception> onError, DateTime runTime)
        {
            bool isThread = false;
            Monitor.Enter(taskLock);
            try
            {
                taskHeap.Push(runTime, new taskInfo { Call = run, OnError = onError });
                if (runTime < nearTime)
                {
                    timer.Stop();
                    nearTime = runTime;
                    double time = (runTime - date.Now).TotalMilliseconds;
                    if (time > 0)
                    {
                        timer.Interval = time;
                        timer.Start();
                    }
                    else isThread = true;
                }
            }
            finally { Monitor.Exit(taskLock); }
            if(isThread) threadPool.FastStart(threadTaskHandle, null, null);
        }
        /// <summary>
        /// 添加任务
        /// </summary>
        /// <param name="run">任务执行委托</param>
        /// <param name="runTime">执行时间</param>
        /// <param name="onError">任务执行出错委托,停止任务参数null</param>
        public void Add(Action run, DateTime runTime, Action<Exception> onError = null)
        {
            if (run != null)
            {
                if (runTime > date.Now) add(run, onError, runTime);
                else threadPool.FastStart(run, null, onError);
            }
        }
        /// <summary>
        /// 添加任务
        /// </summary>
        /// <typeparam name="parameterType">执行参数类型</typeparam>
        /// <param name="run">任务执行委托</param>
        /// <param name="parameter">执行参数</param>
        /// <param name="runTime">执行时间</param>
        /// <param name="onError">任务执行出错委托,停止任务参数null</param>
        public void Add<parameterType>
            (Action<parameterType> run, ref parameterType parameter, DateTime runTime, Action<Exception> onError)
        {
            if (run != null)
            {
                if (runTime > date.Now)
                {
                    run<parameterType> action = run<parameterType>.Pop();
                    add(action.Set(run, ref parameter), onError, runTime);
                }
                else threadPool.FastStart(run, ref parameter, null, onError);
            }
        }
        /// <summary>
        /// 添加任务
        /// </summary>
        /// <typeparam name="parameterType">执行参数类型</typeparam>
        /// <param name="run">任务执行委托</param>
        /// <param name="parameter">执行参数</param>
        /// <param name="runTime">执行时间</param>
        /// <param name="onError">任务执行出错委托,停止任务参数null</param>
        public void Add<parameterType>
            (Action<parameterType> run, parameterType parameter, DateTime runTime, Action<Exception> onError)
        {
            if (run != null)
            {
                if (runTime > date.Now)
                {
                    run<parameterType> action = run<parameterType>.Pop();
                    add(action.Set(run, parameter), onError, runTime);
                }
                else threadPool.FastStart(run, parameter, null, onError);
            }
        }
        /// <summary>
        /// 线程池任务
        /// </summary>
        private void threadTask()
        {
            DateTime now = date.Now;
            Exception exception = null;
            Monitor.Enter(taskLock);
            try
            {
                while (taskHeap.Count != 0)
                {
                    keyValue<DateTime, taskInfo> task = taskHeap.UnsafeTop();
                    if (task.Key <= now)
                    {
                        threadPool.FastStart(task.Value.Call, null, task.Value.OnError);
                        taskHeap.RemoveTop();
                    }
                    else
                    {
                        nearTime = task.Key;
                        timer.Interval = Math.Max((task.Key - now).TotalMilliseconds, 1);
                        timer.Start();
                        break;
                    }
                }
                if (taskHeap.Count == 0) nearTime = DateTime.MaxValue;
            }
            catch (Exception error)
            {
                exception = error;
            }
            finally
            {
                Monitor.Exit(taskLock); 
                if (exception != null)
                {
                    fastCSharp.log.Error.Add(exception, null, false);
                    timer.Interval = 1;
                    timer.Start();
                }
            }
        }
        /// <summary>
        /// 触发定时任务
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void onTimer(object sender, ElapsedEventArgs e)
        {
            threadTask();
        }
        /// <summary>
        /// 默认定时任务
        /// </summary>
        public static readonly timerTask Default = new timerTask(null);
        static timerTask()
        {
            if (fastCSharp.config.appSetting.IsCheckMemory) checkMemory.Add(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        }
    }
}
